package com.mazaiting.rtda.heap;

import com.mazaiting.classfile.constant.entity.NameAndType;
import com.mazaiting.classfile.constant.info.ConstantClassInfo;
import com.mazaiting.classfile.constant.info.ConstantMemberRefInfo;
import lombok.Getter;

import java.util.Objects;

/**
 * 常量
 *
 * @author mazaiting
 * @since 2021/8/16
 */
public class MtConstant {

    /**
     * 类型标识
     */
    @Getter
    protected final short tag;

    private MtConstant(short tag) {
        this.tag = tag;
    }

    public static class MtIntConstant extends MtConstant {
        private final int value;

        public MtIntConstant(short tag, int value) {
            super(tag);
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    public static class MtFloatConstant extends MtConstant {
        private final float value;

        public MtFloatConstant(short tag, float value) {
            super(tag);
            this.value = value;
        }

        public float getValue() {
            return value;
        }
    }

    public static class MtLongConstant extends MtConstant {
        private final long value;

        public MtLongConstant(short tag, long value) {
            super(tag);
            this.value = value;
        }

        public long getValue() {
            return value;
        }
    }

    public static class MtDoubleConstant extends MtConstant {
        private final double value;

        public MtDoubleConstant(short tag, double value) {
            super(tag);
            this.value = value;
        }

        public double getValue() {
            return value;
        }
    }

    public static class MtStringConstant extends MtConstant {
        private final String value;

        public MtStringConstant(short tag, String value) {
            super(tag);
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }

    public static class MtSymRef extends MtConstant {
        /**
         * 常量池
         */
        protected MtConstantPool pool;
        /**
         * 类名
         * 完全限定名
         */
        protected String className;
        /**
         * 类
         */
        protected MtClass mtClass;

        private MtSymRef(short tag) {
            super(tag);
        }

        /**
         * 直接返回类指针，否则调用resolveClassRef（）方法进行解析
         */
        public MtClass resolvedClass() {
            if (Objects.isNull(mtClass)) {
                resolvedClassRef();
            }
            return mtClass;
        }

        /**
         * 解析类引用
         * 如果类D通过符号引用N引用类C的话，要解析N，先用D的类加载器加载C，然后检查D是否有权限访问C，如果没
         * 有，则抛出IllegalAccessError异常
         */
        private void resolvedClassRef() {
            // 获取池中的类引用
            MtClass mtClass = pool.getMtClass();
            // 加载当前类
            MtClass tmpMtClass = mtClass.getLoader().loadClass(className);
            // 判断是否有访问权限
            if (!tmpMtClass.isAccessibleTo(mtClass)) {
                throw new RuntimeException("java.lang.IllegalAccessError");
            }
            this.mtClass = tmpMtClass;
        }
    }

    public static class MtClassRef extends MtSymRef {

        public MtClassRef(short tag, MtConstantPool pool, ConstantClassInfo info) {
            super(tag);
            this.pool = pool;
            this.className = info.name();
        }
    }

    public static class MtMemberRef extends MtSymRef {
        /**
         * 名称
         */
        protected String name;
        /**
         * 描述
         */
        protected String descriptor;

        private MtMemberRef(short tag) {
            super(tag);
        }

        /**
         * 复制字段信息
         *
         * @param info 字段信息
         */
        public void copyMemberRefInfo(ConstantMemberRefInfo info) {
            this.className = info.className();
            NameAndType nameAndType = info.nameAndDescriptor();
            this.name = nameAndType.getName();
            this.descriptor = nameAndType.getType();
        }

        public String getName() {
            return name;
        }

        public String getDescriptor() {
            return descriptor;
        }
    }

    public static class MtFieldRef extends MtMemberRef {
        /**
         * 保存字段引用
         */
        private MtField field;

        public MtFieldRef(short tag, MtConstantPool pool, ConstantMemberRefInfo info) {
            super(tag);
            this.pool = pool;
            copyMemberRefInfo(info);
        }

        /**
         * 加载字段
         *
         * @return 字段
         */
        public MtField resolvedField() {
            if (Objects.isNull(field)) {
                return resolvedFieldRef();
            }
            return field;
        }

        /**
         * 加载字段引用
         * 如果类D想通过字段符号引用访问类C的某个字段，首先要解析符号引用得到类C，然后根据字段名和描述符查找字段。如果字
         * 段查找失败，则虚拟机抛出NoSuchFieldError异常。如果查找成功，但D没有足够的权限访问该字段，则虚拟机抛出IllegalAccessError异常
         *
         * @return 字段
         */
        private MtField resolvedFieldRef() {
            MtClass mtClass = pool.getMtClass();
            MtClass tmpMtClass = resolvedClass();
            // 查找字段
            MtField field = lookupField(tmpMtClass, name, descriptor);
            // 判断是否为空
            if (Objects.isNull(field)) {
                throw new RuntimeException("java.lang.NoSuchFieldError");
            }
            // 判断是否有权限
            if (!field.isAccessibleTo(mtClass)) {
                throw new RuntimeException("java.lang.IllegalAccessError");
            }
            return field;
        }

        /**
         * 查找字段
         * 首先在C的字段中查找。如果找不到，在C的直接接口递归应用这个查找过程。如果还找不到的话，
         * 在C的超类中递归应用这个查找过程。如果仍然找不到，则查找失败。
         *
         * @param mtClass    类实例
         * @param name       名称
         * @param descriptor 描述
         * @return 字段
         */
        private MtField lookupField(MtClass mtClass, String name, String descriptor) {
            // 当前类中查找字段
            for (MtField field : mtClass.getFields()) {
                // 判断名称和描述是否相符
                if (Objects.equals(field.getName(), name) && Objects.equals(field.getDescriptor(), descriptor)) {
                    return field;
                }
            }
            // 接口中查找字段
            for (MtClass classInterface : mtClass.getInterfaces()) {
                return lookupField(classInterface, name, descriptor);
            }
            // 父类中查找字段
            if (Objects.nonNull(mtClass.getSuperClass())) {
                return lookupField(mtClass.getSuperClass(), name, descriptor);
            }
            return null;
        }

        @Override
        public String toString() {
            return "MtFieldRef{" +
                    "className='" + className + '\'' +
                    ", name='" + name + '\'' +
                    ", descriptor='" + descriptor + '\'' +
                    '}';
        }
    }

    public static class MtMethodRef extends MtMemberRef {
        /**
         * 保存方法引用
         */
        private MtMethod method;

        public MtMethodRef(short tag, MtConstantPool pool, ConstantMemberRefInfo info) {
            super(tag);
            this.pool = pool;
            copyMemberRefInfo(info);
        }
    }

    public static class MtInterfaceMethodRef extends MtMemberRef {
        /**
         * 保存方法引用
         */
        private MtMethod method;

        public MtInterfaceMethodRef(short tag, MtConstantPool pool, ConstantMemberRefInfo info) {
            super(tag);
            this.pool = pool;
            copyMemberRefInfo(info);
        }
    }

}


















